#include "trie.hpp"

CTrieNode::CTrieNode() {
    nodes[0] = nodes[1] = NULL;
    asn = 0;
    is_set = false;
}

CTrieNode::CTrieNode(uint32_t _asn, uint32_t prefix_ip, uint8_t prefix_netmask) {
    nodes[0] = nodes[1] = NULL;
    asn = _asn;
//            prefix = new Prefix( prefix_ip, prefix_netmask );
    prefix = Prefix( prefix_ip, prefix_netmask );
    is_set = true;
}

CTrieNode::~CTrieNode() {
//            debugsn( "~CTrieNode " << this->str() );
//            delete prefix;
    delete nodes[0];
    delete nodes[1]; // TODO see if it works as a delete ascade in a tree...
}

//        Prefix* getPrefix() { return prefix; }
//        void setPrefix(Prefix * _prefix) { delete prefix; prefix = _prefix; }

Prefix CTrieNode::getPrefix() { return prefix; }
void CTrieNode::setPrefix(Prefix _prefix) { prefix = _prefix; }

uint32_t CTrieNode::getAsn() { return asn; }
void CTrieNode::setASN(uint32_t _asn ) { asn = _asn; } 

bool CTrieNode::isSet() { return is_set; }
void CTrieNode::isSet(bool x) { is_set = x; }

CTrieNode * CTrieNode::getNode( int i ) { return nodes[i]; }
void CTrieNode::setNode( int i, CTrieNode * node ) { delete nodes[i]; nodes[i] = node; }

string CTrieNode::str() {
    ostringstream oss;
    //if ( prefix ) {
    if ( isSet() ) {
        oss << prefix.str() <<  " " << asn;
        return oss.str();
    }
    else return string("(none)");
}

/*
    -- CTrieTree
 */

CTrieTree::CTrieTree() {   
    root = new CTrieNode();
    stats_nodes_all = stats_nodes_set = 0;
}
CTrieTree::~CTrieTree() {
    delete root;
}

/* return -1 if tries to overwrite existing leaf */
int CTrieTree::insertNode( uint32_t asn, Prefix prefix, uint32_t flags ) {
    CTrieNode *nextnode, *currentnode = root;
    int32_t b,i=0;

//            debugsn("CTrieTree::insertNode asn " << asn << " prefix " << prefix->str() );
    /* for bits in the prefix from MSB to LSB, go down the tree and
       create the nodes, until the leaf is reached */
    b = prefix.getBit(i);
    while( b >= 0 ) {
//                debugsn("CTrieTree::insertNode nbit " << i << " bit " << b);
        /* get prefix bit and fetch the trie node for that bit */
        nextnode = currentnode->getNode(b);
        /* if node does not exist, create it */
        if ( nextnode == NULL ) {
            currentnode->setNode( b , new CTrieNode() );
            nextnode = currentnode->getNode(b);
//                    debugsn("CTrieTree::insertNode new node");
            stats_nodes_all++;
        }
        currentnode = nextnode;

        i++;
        b = prefix.getBit(i);
    }

    /* check if leaf exists */
    if (  (!(flags & CTrieTree::FORCENODE)) & ( currentnode->isSet() ) ) {
        return -1;
    }

    /* for the leaf, set asn and prefix
       check if any new node was created */
    currentnode->setASN(asn);
    currentnode->setPrefix( prefix );
    currentnode->isSet( true );

    stats_nodes_set++;
//            debugsn("CTrieTree::insertNode created asn " << currentnode->getAsn() << " prefix " << currentnode->getPrefix()->str() );

    return 0;
}

/*
 -- lpmatch
 longest prefix match
 network order, MSB first
 */
CTrieNode * CTrieTree::lpmatch( uint32_t ip , uint8_t mask) {
    CTrieNode *node = root, *node_ret = NULL;
    uint32_t b;
    //if ( root->getPrefix() != NULL ) node_ret = root;
    if ( root->isSet() ) node_ret = root;

    for ( int i = 0; i < mask; i++ ) {
//    for ( int i = 0; i < 32; i++ ) {
        /* move to the next node 0/1 */
        b = ( ip & 0x80000000 ) ? 1 : 0; /* msb */

        node = node->getNode( b );
        if ( node == NULL) break;
//                if ( node->getPrefix() != NULL ) node_ret = node;
        if ( node->isSet() ) node_ret = node;

        ip = ip << 1;
    }
    return node_ret;
}


void CTrieTree::addSpecial() {
    addReservedPrefixes( this );
}

int CTrieTree::addFromFile( string fname, uint32_t format, uint32_t flags ) {
    ifstream file;
    uint32_t ip_byte3, ip_byte2, ip_byte1, ip_byte0, netmask, asn, r;
    char buf[256];

    file.open( fname.c_str() );

    if ( ! file.is_open() ) { warn("Could not open trie file " << fname); return CTrieTree::ERR_TRIE_FILE_OPEN; }

    /* text format of the prefix - asn */
    if ( format == FORMATTXT ) {
        while ( !file.eof() ) {
            file.getline( buf, 256 );
            if ( buf[0] == '#' ) continue;
//                    debugsn( "newFromFile " << string(buf) );
            r = sscanf(buf, "%d.%d.%d.%d/%d %d", &ip_byte3, &ip_byte2, &ip_byte1, &ip_byte0, &netmask, &asn);
            if ( r == 6 )  {
                if ( insertNode( asn, Prefix( bytes_4_to_uint32(ip_byte3, ip_byte2, ip_byte1, ip_byte0) , netmask) , flags) ) {
                    /* leaf was not created, entry already there */
                    warn("Leaf not created, already present!" << string(buf) );
                    return CTrieTree::ERR_LEAF_PRESENT;
                }
            }
            else if (r == EOF) {/*will break at next loop*/}
            else {
                warn("Problems with line '" << string(buf) << r << "'") ;
                return CTrieTree::ERR_PROBLEM_WITH_LINE;
            }
        }
    }
    else {
        file.close();
        warn("Unknown CTrieTree file format");
        return CTrieTree::ERR_TRIE_FILE_FORMAT;
    }
    file.close();

    return CTrieTree::ERR_OK;
}

uint32_t CTrieTree::getStatsNodesAll() { return stats_nodes_all; }
uint32_t CTrieTree::getStatsNodesSet() { return stats_nodes_set; }

/** ** static ** **/

CTrieTree * CTrieTree::newFromFile( string fname, uint32_t format ) {
    CTrieTree * ttree;
    ttree = new CTrieTree();
    if ( ttree->addFromFile( fname, format ) != CTrieTree::ERR_OK ) {
        delete ttree;
        ttree = NULL;
    }
    return ttree;
}



void CTrieTree::addReservedPrefixes( CTrieTree * t ) { 
    uint32_t f=CTrieTree::FORCENODE;
    t->insertNode(0, Prefix( bytes_4_to_uint32( 0,0,0,0      ), 0) , f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 10,0,0,0     ), 8) , f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 127,0,0,0    ), 8) , f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 169,254,0,0  ), 16), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 172,16,0,0   ), 12), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 192,0,2,0    ), 24), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 192,88,99,0  ), 24), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 192,168,0,0  ), 16), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 198,18,0,0   ), 15), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 198,51,100,0 ), 24), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 203,0,113,0  ), 24), f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 224,0,0,0    ), 4) , f );
    t->insertNode(0, Prefix( bytes_4_to_uint32( 240,0,0,0    ), 4) , f );
}



